Merge pull request #437 from PetroFeed/add_change_detector_agent

Add ChangeDetectorAgent, test

Andrew Cantino лет %!s(int64=10): %!d(string=назад)
Родитель
Сommit
b2213f2b17
2 измененных файлов с 174 добавлено и 0 удалено
  1. 76 0
      app/models/agents/change_detector_agent.rb
  2. 98 0
      spec/models/agents/change_detector_agent_spec.rb

+ 76 - 0
app/models/agents/change_detector_agent.rb

@@ -0,0 +1,76 @@
1
+module Agents
2
+  class ChangeDetectorAgent < Agent
3
+    cannot_be_scheduled!
4
+
5
+    description <<-MD
6
+      The ChangeDetectorAgent receives a stream of events and emits a new event when a property of the received event changes.
7
+
8
+      `property` specifies the property to be watched.
9
+
10
+      `expected_update_period_in_days` is used to determine if the Agent is working.
11
+
12
+      The resulting event will be a copy of the received event.
13
+    MD
14
+
15
+    event_description <<-MD
16
+    This will change based on the source event. If you were event from the ShellCommandAgent, your outbound event might look like:
17
+
18
+      {
19
+        'command' => 'pwd',
20
+        'path' => '/home/Huginn',
21
+        'exit_status' => '0',
22
+        'errors' => '',
23
+        'output' => '/home/Huginn'
24
+      }
25
+    MD
26
+
27
+    def default_options
28
+      {
29
+          'property' => '{{output}}',
30
+          'expected_update_period_in_days' => 1
31
+      }
32
+    end
33
+
34
+    def validate_options
35
+      unless options['property'].present? && options['expected_update_period_in_days'].present?
36
+        errors.add(:base, "The property and expected_update_period_in_days fields are all required.")
37
+      end
38
+    end
39
+
40
+    def working?
41
+      event_created_within?(interpolated['expected_update_period_in_days']) && !recent_error_logs?
42
+    end
43
+
44
+    def receive(incoming_events)
45
+      incoming_events.each do |event|
46
+        handle(interpolated(event), event)
47
+      end
48
+    end
49
+
50
+    private
51
+
52
+    def handle(opts, event = nil)
53
+      property = opts['property']
54
+      if has_changed?(property)
55
+        created_event = create_event :payload => event.payload
56
+
57
+        log("Propagating new event as property has changed to #{property} from #{last_property}", :outbound_event => created_event, :inbound_event => event )
58
+        update_memory(property)
59
+      else
60
+        log("Not propagating as incoming event has not changed from #{last_property}.", :inbound_event => event )
61
+      end
62
+    end
63
+
64
+    def has_changed?(property)
65
+      property != last_property
66
+    end
67
+
68
+    def last_property
69
+      self.memory['last_property']
70
+    end
71
+
72
+    def update_memory(property)
73
+      self.memory['last_property'] = property
74
+    end
75
+  end
76
+end

+ 98 - 0
spec/models/agents/change_detector_agent_spec.rb

@@ -0,0 +1,98 @@
1
+require 'spec_helper'
2
+
3
+describe Agents::ChangeDetectorAgent do
4
+  def create_event(output=nil)
5
+    event = Event.new
6
+    event.agent = agents(:jane_weather_agent)
7
+    event.payload = {
8
+      :command => 'some-command',
9
+      :output => output
10
+    }
11
+    event.save!
12
+
13
+    event
14
+  end
15
+
16
+  before do
17
+    @valid_params = {
18
+        :property  => "{{output}}",
19
+        :expected_update_period_in_days => "1",
20
+      }
21
+
22
+    @checker = Agents::ChangeDetectorAgent.new(:name => "somename", :options => @valid_params)
23
+    @checker.user = users(:jane)
24
+    @checker.save!
25
+  end
26
+
27
+  describe "validation" do
28
+    before do
29
+      @checker.should be_valid
30
+    end
31
+
32
+    it "should validate presence of property" do
33
+      @checker.options[:property] = nil
34
+      @checker.should_not be_valid
35
+    end
36
+
37
+    it "should validate presence of property" do
38
+      @checker.options[:expected_update_period_in_days] = nil
39
+      @checker.should_not be_valid
40
+    end
41
+  end
42
+
43
+  describe "#working?" do
44
+    before :each do
45
+      # Need to create an event otherwise event_created_within? returns nil
46
+      event = create_event
47
+      @checker.receive([event])
48
+    end
49
+
50
+    it "is when event created within :expected_update_period_in_days" do
51
+      @checker.options[:expected_update_period_in_days] = 2
52
+      @checker.should be_working
53
+    end
54
+
55
+    it "isnt when event created outside :expected_update_period_in_days" do
56
+      @checker.options[:expected_update_period_in_days] = 2
57
+
58
+      time_travel_to 2.days.from_now do
59
+          @checker.should_not be_working
60
+      end
61
+    end
62
+  end
63
+
64
+  describe "#receive" do
65
+    before :each do
66
+      @event = create_event("2014-07-01")
67
+    end
68
+
69
+    it "creates events when memory is empty" do
70
+      @event.payload[:output] = "2014-07-01"
71
+      expect {
72
+        @checker.receive([@event])
73
+      }.to change(Event, :count).by(1)
74
+      Event.last.payload[:command].should == @event.payload[:command]
75
+      Event.last.payload[:output].should == @event.payload[:output]
76
+    end
77
+
78
+    it "creates events when new event changed" do
79
+      @event.payload[:output] = "2014-07-01"
80
+      @checker.receive([@event])
81
+
82
+      event = create_event("2014-08-01")
83
+
84
+      expect {
85
+        @checker.receive([event])
86
+      }.to change(Event, :count).by(1)
87
+    end
88
+
89
+    it "does not create event when no change" do
90
+      @event.payload[:output] = "2014-07-01"
91
+      @checker.receive([@event])
92
+
93
+      expect {
94
+        @checker.receive([@event])
95
+      }.to change(Event, :count).by(0)
96
+    end
97
+  end
98
+end